Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Another Enum Viewer

0.00/5 (No votes)
22 Oct 2001 2  
An article on the usage and design of another Enum Viewer

Another Enum Viewer

Another Enum Viewer

This article explains the usage and development of an enum viewer tool.

Usage

Download the enum viewer's .exe's into the same directory. Run enum_viewer. Open a header file. Click on the column buttons to sort.

Development

1. The problem

My day job is writing software that talks to embedded devices via Win32 communication channels (rs232, rs485, tcp/ip, etc.). In order to minimize backwards compatibility and protocol flexibility, we designed a message passing protocol using lengths and identifiers. Messages all have a unique identifier in a 16 bit enum value defined in a header file. I am in the process of writing a device simulator and wanted the enum values when looking at raw messages. None of the tools I've seen handle comments, #defines, enums within classes, enums with enum assignments, etc. The Visual C++ IDE, browser and debugger don't show enum values (the debugger does one at a time if typedef'd).

2. The solution

I figured that there must be some C++ parser out there somewhere that might provide a basis for an enum viewer. A quick search on google turned up John's PCCTS-based C++ Parser Page.

Neat! A C++ parser! I downloaded and attempted to build the code but the compilation stopped with error messages regarding STL allocators. After trying to coax the parser into using STLPort, I gave up and used the default MSVC STL and it all compiled.

Then I turned to the docs (some of you are probably snickering right now, stop it). After looking through the docs I got a little frustrated and had a brief flirtation with regular expressions. Thinking about all ways of declaring / defining enums chased me back to the parser. Looking at the directories again I noticed a sample application called cpp_test. I compiled and ran it against a header with a lot of enums. The results were encouraging, all of the enum's names were displayed along with their recursive values eRED = (((10)+1)+1).

Digging into the code, I found the dumping functions and looked up the definitions of the various class members thanks to Joshua C. Jensen's very fine Workspace Whiz.

I only had to derive two new classes, move a function from private to protected, and I had a command line app that could turn a header into an ascii file of enums and their values.

3. Displaying the data

As cool as that was, I still wanted to sort enum names or their values. I wanted a GUI capable of browsing, sorting and displaying data in hex. Hmm... I've done quite a bit of work using ListView controls in the last couple of years and this seemed a good fit (just like everything looks like a nail when you're holding a hammer). I generated an AppWizard MFC SDI app and started working on the parsing of comma separated value files.

I decided to make the ListView virtual for these three reasons in order:

  • More flexible, all formatting is done at the time of display
  • Less memory Usage
  • Sort works much faster

The whole point of a virtual ListView is so the application maintains the string representations of the listview in memory. In that end, I used a two dimensional std::vector of CStrings to cache data from the document. I use the WM_NOTIFY message LVN_ODCACHEHINT to initialize the size and content of the cache. The ListView responds to the WM_NOTIFY message LVN_GETDISPINFO by handing out pointers to cached CString's buffers via GetBuffer(). I could have used std::string, but did not want to convert integers into decimal and hex using std::stringstream when a simple call to CString::Format could be used. For more details, see the code in enum_view.cpp. Having settled the view, I moved on to implementing the document.

Two important things that virtual views need are the row count for initialization and individual row access to feed the cache.

Counting Rows

Each row in the listview corresponds to a line in the parser output file. Each line looks like:

enum name,value\r\n

Therefore, the number of \n's in the document is the number of records. An easy way to do this using the C++ standard library:

size_t lineCount = 0;

// ignore all characters up to '\n', extract '\n', toss it, advance

while ( m_fileIn.ignore(std::numeric_limits::max(), '\n') )
    ++lineCount; 

Where m_fileIn is any istream.

Individual Row Access

This is a no brainer for C++ iostreams. One of the nice things about using iostream extractors is that the status of an extraction can be implied from doing a boolean test on the extraction itself. See Marshall Cline's excellent C++ FAQ for details.

// in case the stream was in an error state (at the end of file for example)

m_fileIn.clear();

// using an index is waaaaay faster.

m_fileIn.seekg( fileOffset );

std::string strName("");
parsed = std::getline(m_fileIn, strName, ',') && m_fileIn >> value;

// Convert std::string into CString

if (parsed)
    name = strName.c_str();

On my first cut at getting the records, I seeked the file to the beginning, then counted up to the requested index. This made the view pretty slow! I sped things up considerably by making an index of file offsets and seeking to them on demand.

I then wired up the request for open file to spawn a new process of the enum parser passing it a temporary file name.

Sorting

I read this really useful tip in the C/C++ Users Journal about a generic STL index creation function. Herb Sutter did a good source code review on the code over at Guru of the Week with many improvements. I implemented a few of them and added one of my own (see the code for details). The function create_index takes _any_ sequence of iterators, applies a sort on them, and fills in index numbers of the sort result. The view keeps around one of these index sequences and uses it to display the data. If the current column is already sorted, then the index order is simply reversed. All thanks to the beautifully orthogonal STL.

Most Recently Used File

Since the CDocument uses temp files, the MRU list gets all these temp file names that no longer exist. The app removes these and adds the header name instead.

That's it. Please check out the code if you have any questions. If you find any bugs, let me know too. I might even fix them. =)


As an aside, if given the choice between using an MFC or a standard C++ library method, I'll prefer the standard. My reasoning is that:

  • 3rd Party documentation for the standard library is much better (I'll take Austern, Josuttis, et al. over any MFC author)
  • Standard library interfaces are much more consistent and orthogonal
  • More flexible platform / compiler support

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here